/**
 * Copyright (c), 上海沃恩信息科技有限公司.
 * All rights reserved.
 * Licensed under BSD (https://www.pittypat.work/bsd.html).
 */

package pittypat;

import java.io.IOException;
import java.io.Writer;
import java.util.AbstractList;
import java.util.ArrayList;

import pittypat.data.DbDataReader;

/**
 * DataModel 对象的集合。该集合转换为 JSON 时被转换为对象的数组。
 *
 */
public class DataList<T extends DataModel> extends AbstractList<T> implements IComplex {
	private Class<?> itemType;
	private ArrayList<T> items;

	/**
	 * 使用集合元素的类型初始化 DataList 的一个新实例。
	 * 
	 * @param itemType
	 *            集合元素的类型。
	 */
	public DataList(Class<?> itemType) {
		super();

		if (itemType == null) {
			throw new IllegalArgumentException("itemType 为 null。");
		}

		if (!DataModel.class.isAssignableFrom(itemType)) {
			throw new IllegalArgumentException(itemType.getName() + " 不是一个 DataModel 类型。");
		}

		this.items = new ArrayList<T>(20);
		this.itemType = itemType;
	}

	/**
	 * 使用初始容量和集合元素的类型初始化 DataList 的一个新实例。
	 * 
	 * @param capacity
	 *            集合的初始容量。
	 * @param itemType
	 *            集合元素的类型。
	 */
	public DataList(int capacity, Class<?> itemType) {
		super();

		if (itemType == null) {
			throw new IllegalArgumentException("itemType 为 null。");
		}

		if (!DataModel.class.isAssignableFrom(itemType)) {
			throw new IllegalArgumentException(itemType.getName() + " 不是一个 DataModel 类型。");
		}

		this.items = new ArrayList<T>(capacity < 0 ? 20 : capacity);
		this.itemType = itemType;
	}

	/**
	 * 获取集合元素的类型。
	 * 
	 * @return 集合元素的类型。
	 */
	public Class<?> getItemType() {
		return this.itemType;
	}

	/**
	 * 获取指定位置上的元素。
	 * 
	 * @param i
	 *            元素的位置。
	 */
	@Override
	public T get(int i) {
		return this.items.get(i);
	}

	/**
	 * 获取集合中已经添加的元素个数。
	 * 
	 * @return 集合中已经添加的元素的数目。
	 */
	@Override
	public int size() {
		return this.items.size();
	}

	/**
	 * 设置指定位置上的元素。
	 * 
	 * @param i
	 *            元素的位置。
	 * @param item
	 *            要设置的元素值。
	 * @return 在目标位置上的原来的元素。
	 */
	@Override
	public T set(int i, T item) {
		return this.items.set(i, item);
	}

	/**
	 * 在指定位置上添加一个新的元素，后续元素位置逐次后移。
	 * 
	 * @param i
	 *            元素的位置。
	 * @param item
	 *            要添加的元素值。
	 */
	@Override
	public void add(int i, T item) {
		this.items.add(i, item);
	}

	/**
	 * 从集合中移除指定位置上的元素。
	 * 
	 * @return 在目标位置上的原来的元素。
	 */
	@Override
	public T remove(int i) {
		return this.items.remove(i);
	}

	/**
	 * 判断集合中是否存在满足指定条件的元素。
	 * 
	 * @param where
	 *            判断条件。
	 * @return 如果集合中存在满足指定条件的元素，则返回 true，否则返回 false。
	 */
	public boolean exists(Where<T> where) {
		int i = 0;
		for (T item : this.items) {
			if (where.test(item, i++)) {
				return true;
			}
		}

		return false;
	}

	/**
	 * 返回集合中满足条件的第一个元素，如果没有找到，则返回指定的默认值。
	 * 
	 * @param where
	 *            判断条件。
	 * @param defaultValue
	 *            当没有找到时应该返回的值。
	 * @return 集合中满足条件的第一个元素。如果没有符合条件的元素，则返回 defaultValue。
	 */
	public T find(Where<T> where, T defaultValue) {
		int i = 0;
		for (T item : this.items) {
			if (where.test(item, i++)) {
				return item;
			}
		}

		return defaultValue;
	}

	/**
	 * 返回集合中满足条件的第一个元素。等价于 find(where, null)。
	 * 
	 * @param where
	 *            判断条件。
	 * @return 集合中满足条件的第一个元素。如果没有符合条件的元素，则返回 null。
	 */
	public T find(Where<T> where) {
		return find(where, null);
	}

	/**
	 * 返回集合中满足条件的第一个元素的位置。
	 * 
	 * @param where
	 *            判断条件。
	 * @return 集合中满足条件的第一个元素的位置。如果没有符合条件的元素，则返回 -1。
	 */
	public int findIndex(Where<T> where) {
		int i = 0;
		for (T item : this.items) {
			if (where.test(item, i++)) {
				return i;
			}
		}

		return -1;
	}

	/**
	 * 使用指定的条件搜索集合，并返回满足条件的所有元素的集合。
	 * 
	 * @param where
	 *            搜索条件。
	 * @return 满足条件的所有元素的集合。如果没有满足条件的元素，则返回一个空集合。
	 */
	public ArrayList<T> search(Where<T> where) {
		ArrayList<T> result = new ArrayList<T>();
		int i = 0;
		for (T item : this.items) {
			if (where.test(item, i++)) {
				result.add(item);
			}
		}

		return result;
	}

	/**
	 * 将当前对象作为 JSON 字符串写入指定的文本流。
	 * 
	 * @param writer
	 *            目标文本流。
	 * @throws IllegalArgumentException
	 *             writer 为 null。
	 * @throws IOException
	 */
	@Override
	public void write(Writer writer) throws IOException {
		if (writer == null) {
			throw new IllegalArgumentException("writer 为 null。");
		}

		int count = this.items.size();
		writer.write('[');

		if (count > 0) {
			// 第一个元素
			writeItem(this.items.get(0), writer);

			if (count > 1) {
				for (int i = 1; i < count; ++i) {
					writer.write(',');
					writeItem(this.items.get(i), writer);
				}
			}
		}

		writer.write(']');
	}

	/**
	 * 向目标 Writer 中写入一个元素。如果元素为 null，则写入 null。
	 */
	private void writeItem(T item, Writer writer) throws IOException {
		if (item == null) {
			writer.write("null");
		} else {
			item.write(writer);
		}
	}

	/**
	 * 从 DbDataReader 中读取一个或多个对象。不会事先清空集合。
	 * 
	 * @param reader
	 *            目标 DbDataReader 对象。
	 * @return 读取并添加到集合中的对象总数目，如果集合原来不是空的，返回的数目将小于集合元素的数目。
	 * @throws IllegalAccessException
	 * @throws InstantiationException
	 * @throws IllegalArgumentException
	 *             reader 为 null。
	 */
	@SuppressWarnings("unchecked")
	public int read(DbDataReader reader) throws InstantiationException, IllegalAccessException {
		if (reader == null) {
			throw new IllegalArgumentException("reader 为 null。");
		}

		T item = null;
		int count = 0;

		while (reader.read()) {
			item = (T) this.itemType.newInstance();
			item.read(reader);
			this.items.add(item);
			++count;
		}

		return count;
	}
}
